import { Directive, DirectiveBinding, nextTick } from "vue"
import { debounce } from "lodash-es"

/**
 * 检测元素类型并返回对应的滚动容器
 * @param el 绑定指令的元素
 * @returns 滚动容器元素
 */
function getScrollContainer(el: HTMLElement): HTMLElement | null {
  // 检查是否为 Element Plus 下拉框
  const popperClassName = el.getAttribute("popper-class-name")
  if (popperClassName) {
    // Element Plus 下拉框场景
    const selectDom = document.querySelector(`.${popperClassName} .el-select-dropdown__wrap`) as HTMLElement
    return selectDom
  }

  // 检查是否为 Element Plus 表格
  if (el.classList.contains("el-table__body-wrapper")) {
    return el
  }

  // 检查父元素是否为 Element Plus 表格
  const tableWrapper = el.closest(".el-table__body-wrapper") as HTMLElement
  if (tableWrapper) {
    return tableWrapper
  }

  // 普通 DOM 元素场景 - 检查元素本身是否有滚动
  const computedStyle = window.getComputedStyle(el)
  const hasScroll =
    computedStyle.overflowY === "auto" ||
    computedStyle.overflowY === "scroll" ||
    computedStyle.overflow === "auto" ||
    computedStyle.overflow === "scroll"

  if (hasScroll) {
    return el
  }

  // 如果元素本身没有滚动，查找最近的可滚动父元素
  let parent = el.parentElement
  while (parent && parent !== document.body) {
    const parentStyle = window.getComputedStyle(parent)
    const parentHasScroll =
      parentStyle.overflowY === "auto" || parentStyle.overflowY === "scroll" || parentStyle.overflow === "auto" || parentStyle.overflow === "scroll"

    if (parentHasScroll) {
      return parent
    }
    parent = parent.parentElement
  }

  // 最后回退到 window 滚动
  return document.documentElement
}

/**
 * 判断是否滚动到底部
 * @param container 滚动容器
 * @param threshold 触发阈值，默认 30px
 * @returns 是否到达底部
 */
function isScrollToBottom(container: HTMLElement, threshold: number = 30): boolean {
  if (container === document.documentElement) {
    // window 滚动情况
    return window.innerHeight + window.scrollY >= document.body.offsetHeight - threshold
  } else {
    // 元素滚动情况
    return container.scrollHeight - container.scrollTop - threshold <= container.clientHeight
  }
}

const loadMore: Directive = {
  beforeMount(el: any, binding: DirectiveBinding) {
    // 获取滚动容器
    const scrollContainer = getScrollContainer(el)

    if (!scrollContainer) {
      console.warn("loadMore directive: 未找到可滚动的容器")
      return
    }

    // 获取配置参数
    const options = typeof binding.value === "object" ? binding.value : { callback: binding.value }
    const { callback, threshold = 30, debounceDelay = 200 } = options

    function loadMores() {
      // 判断是否滚动到底部
      const isAtBottom = isScrollToBottom(scrollContainer as HTMLElement, threshold)
      if (isAtBottom) {
        callback && callback() // 触发绑定的回调函数
      }
    }

    // 使用防抖包装函数，避免频繁触发
    const debouncedLoadMore = debounce(loadMores, debounceDelay)

    // 存储信息到元素上
    el._loadMoreInfo = {
      container: scrollContainer,
      handler: debouncedLoadMore
    }

    nextTick(() => {
      if (scrollContainer === document.documentElement) {
        // window 滚动事件
        window.addEventListener("scroll", debouncedLoadMore)
      } else {
        // 元素滚动事件
        scrollContainer.addEventListener("scroll", debouncedLoadMore)
      }
    })
  },
  beforeUnmount(el: any) {
    if (el._loadMoreInfo) {
      const { container, handler } = el._loadMoreInfo

      if (container === document.documentElement) {
        window.removeEventListener("scroll", handler)
      } else {
        container?.removeEventListener("scroll", handler)
      }

      delete el._loadMoreInfo
    }
  }
}

export { loadMore }
